import os
import sys
import shutil
import json

from utils.common_utils import str_md5, get_binary_path
from utils.db_object_pool import DBObjectPool
from utils.singleton import Singleton

class Context(metaclass=Singleton):
    def __init__(self):
        
        self.IRGEN_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
        self.IRGEN_WORK_PATH = ".IRGEN"
        
        self.IR_DIR = "ir"
        self.MAPLE_DIR = "maple"
        self.AST_DIR = "ast"
        self.DB_DIR = "db"
        self.LOG_DIR = "log"
        self.CACHE_DIR = "cache"
        self.ERROR_DIR = "error"
        self.BUILD_GRAPH_DIR = "graph"
        self.NAME_DEP = "name_dep.json"
        self.MID_TEMP_DIR = "temp"
        self.REPORT_DIR = "report"
        self.OBJ_DIR = "obj"
        self.ASSEMBLE_DIR = "assemble"
        self.TOP_DIR = "top"

        self.IR_EXT = ".bc"
        self.MAPLE_EXT = ".mpl"
        self.AST_EXT = ".ast"
        self.CACHE_EXT = ".src"
        self.OBJ_EXT = ".o"
        self.ASSEMBLE_EXT = ".s"
        
        self.ARCH_DIC = os.path.join(self.IRGEN_PATH, "configure/arch_cpu_feature_map.json")
        self.COMPILER_NAMES_FILE = os.path.join(self.IRGEN_PATH, "configure/compiler_toolchain.xml")
        self.CAPTURE_LIB = "libcapture.so"
        self.CACHE_LIB = os.path.join(self.IRGEN_PATH, "capture/libfilecache.so")
        self.LD_LIB_PATH = ':'.join([os.path.join(self.IRGEN_PATH, "capture/lib64"), os.path.join(self.IRGEN_PATH, "capture/lib32")])
        
        # redis config
        self.REDIS_SERVER = "redis-server"
        self.REDIS_CAPTURE_DB_NAME = "capture"
        self.REDIS_CACHE_DB_NAME = "cache"
        self.REDIS_CAPTURE_DB_SOCKET = ""
        self.REDIS_CACHE_DB_SOCKET = ""
        
        # Maple root path
        self.MAPLE_ROOT = ""
        self.MAPLE_TOOLS = ""
        # self.MAPLE_BUILD_OUT = "/TalonInArkCompiler/output/aarch64-clang-release"
        # self.MAPLE_ROOT = "/TalonInArkCompiler"
        # self.MAPLE_TOOLS = "/TalonInArkCompiler/tools"
        # self.MAPLE_BUILD_OUT = "/TalonInArkCompiler/output/aarch64-clang-release"
        # self.MAPLE_ROOT = "/root/newMaple/OpenArkCompiler"
        # self.MAPLE_TOOLS = "/root/newMaple/OpenArkCompiler/tools"
        # self.MAPLE_BUILD_OUT = "/root/newMaple/OpenArkCompiler/output/aarch64-clang-release"
        
        # target binary path
        self.CLANG = ""
        self.CLANGCPP = ""
        # prefix_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        # self.MAPLE_ROOT = os.path.join(prefix_path,"/TalonInArkCompiler")
        # self.MAPLE_TOOLS = os.path.join(prefix_path,"/TalonInArkCompiler/tools")
        # self.MAPLE_TOOLS = os.path.join(prefix_path,"/TalonInArkCompiler/output/aarch64-clang-release")
        
        # self.MAPLE_CLANG = os.path.join(prefix_path,"Maple/OpenArkCompiler/tools/bin/clang")
        # self.MAPLE_CLANGCPP = os.path.join(prefix_path,"Maple/OpenArkCompiler/tools/bin/clang++")
        # self.MAPLE_BIN =      os.path.join(prefix_path,"Maple/OpenArkCompiler/output/aarch64-clang-release/bin/hir2mpl")
        # self.MAPLE_GCC =      os.path.join(prefix_path,"Maple/OpenArkCompiler/output/tools/gcc-linaro-7.5.0/bin/aarch64-linux-gnu-gcc")
        # self.MAPLE_MAPLE =    os.path.join(prefix_path,"Maple/OpenArkCompiler/output/aarch64-clang-release/bin/maple")

        # self.MAPLE_CLANG = "/TalonInArkCompiler/tools/bin/clang"
        # self.MAPLE_CLANGCPP = "/TalonInArkCompiler/tools/bin/clang++"
        # self.MAPLE_BIN = "/TalonInArkCompiler/output/aarch64-clang-release/bin/hir2mpl"
        # self.MAPLE_GCC = "/TalonInArkCompiler/output/tools/gcc-linaro-7.5.0/bin/aarch64-linux-gnu-gcc"
        # self.MAPLE_MAPLE = "/TalonInArkCompiler/output/aarch64-clang-release/bin/maple"
        
        
        # another configuation
        self.MAPLE_CLANG = "/root/newMaple/OpenArkCompiler/tools/bin/clang"
        self.MAPLE_CLANGCPP = "/root/newMaple/OpenArkCompiler/tools/bin/clang++"
        self.MAPLE_BIN = "/root/newMaple/OpenArkCompiler/output/aarch64-clang-release/bin/hir2mpl"
        self.MAPLE_GCC = "/root/newMaple/OpenArkCompiler/tools/gcc-linaro-7.5.0/bin/aarch64-linux-gnu-gcc"
        self.MAPLE_MAPLE = "/root/newMaple/OpenArkCompiler/output/aarch64-clang-release/bin/maple"
        
        # compilers version
        self.GCC_VERSION = ""
        self.GCCPLUS_VERSION = ""
        self.CLANG_VERSION = ""
        self.CLANGCPP_VERSION = ""
        self.LD_VERSION = ""
        self.AR_VERSION = ""
        self.RANLIB_VERSION = ""
        self.STRIP_VERSION = ""
        
        # file ext
        self.AST_EXT = ".ast"
        self.BC_EXT = ".bc"
        self.MAPLE_EXT = ".mpl"
                
        # logger setting
        self.LOG_NAME = "IRGen.log"
        self.CAP_LOG_NAME = "capture.log"
        self.ENABLE_LOG_PRINT = False
        
        
        # running config
        self.ENABLE_BUILD_GRAPH_DUMP = False       
        self.BUILD_GRAPH_DUMP = "build_graph.png"
        self.CAPTURE_ONLY = False
        self.IRBUILD_ONLY = False
        self.CLEAR_WORK_DIRS = False
        self.BUILD_POOL_SIZE = -1 # -1 dicates no initial
        self.IR_CAT_MODE = False
        self.ENABLE_DEBUG_MODE = False
        self.ENABLE_WHOLE_PROCESS = False
    
        
        # custom things
        self.CUSTOM_INCLUDE_PATH = [
            self.MAPLE_ROOT + "/lib/libc_enhanced/include",
            self.MAPLE_ROOT + "/output/aarch64-clang-release/lib/include",
            self.MAPLE_ROOT + "/output/aarch64-clang-release/lib",
            ]
        
        
    def update_work_path(self, work_path):    
        self.IRGEN_WORK_PATH = os.path.abspath(os.path.join(work_path, self.IRGEN_WORK_PATH))
        self._update_context()
        
    
    def _update_context(self):
        
        self.IR_DIR = os.path.join(self.IRGEN_WORK_PATH, self.IR_DIR)
        self.MAPLE_DIR = os.path.join(self.IRGEN_WORK_PATH, self.MAPLE_DIR)
        self.AST_DIR = os.path.join(self.IRGEN_WORK_PATH, self.AST_DIR)
        self.DB_DIR = os.path.join(self.IRGEN_WORK_PATH, self.DB_DIR)
        self.LOG_DIR = os.path.join(self.IRGEN_WORK_PATH, self.LOG_DIR)
        self.CACHE_DIR = os.path.join(self.IRGEN_WORK_PATH, self.CACHE_DIR)
        self.ERROR_DIR = os.path.join(self.IRGEN_WORK_PATH, self.ERROR_DIR)
        self.MID_TEMP_DIR = os.path.join(self.IRGEN_WORK_PATH, self.MID_TEMP_DIR)
        self.BUILD_GRAPH_DIR = os.path.join(self.IRGEN_WORK_PATH, self.BUILD_GRAPH_DIR)
        self.REPORT_DIR = os.path.join(self.IRGEN_WORK_PATH, self.REPORT_DIR)
        self.OBJ_DIR = os.path.join(self.IRGEN_WORK_PATH, self.OBJ_DIR)
        self.ASSEMBLE_DIR = os.path.join(self.IRGEN_WORK_PATH, self.ASSEMBLE_DIR)
        self.TOP_DIR = os.path.join(self.IRGEN_WORK_PATH, self.TOP_DIR)
        self.NAME_DEP = os.path.join(self.IRGEN_WORK_PATH, self.NAME_DEP)
        
        self.CLANG = get_binary_path("clang")
        self.CLANGCPP = get_binary_path("clang++")
        
    def check_redis_cached_config(self):
        """ reuse cached redis configure if it exists """
        if self.REDIS_CAPTURE_DB_SOCKET and self.REDIS_CACHE_DB_SOCKET:
            return 
        sock_list = self._load_socket()
        if not sock_list:
            captrue_sock = os.path.join(self.DB_DIR, self._socket_name(self.REDIS_CAPTURE_DB_NAME)) + ".sock"
            cache_sock = os.path.join(self.DB_DIR, self._socket_name(self.REDIS_CACHE_DB_NAME)) + ".sock"
        else:
            captrue_sock = sock_list["capture"]
            cache_sock = sock_list["cache"]
        self._set_db_sockets(cap_sock=captrue_sock, cache_sock=cache_sock)
        self._store_socket()
        
        DBObjectPool.set_sockets(cap_sock=self.REDIS_CAPTURE_DB_SOCKET, cache_sock=self.REDIS_CACHE_DB_SOCKET)
        
        
    def _socket_name(self, path):
        """ calculate socket name """
        return str_md5(path)
    
    def _set_db_sockets(self, cap_sock="", cache_sock=""):
        self.REDIS_CAPTURE_DB_SOCKET = cap_sock
        self.REDIS_CACHE_DB_SOCKET = cache_sock
    
    def _load_socket(self):
        """ load cached socket name """
        cache_path = os.path.join(self.DB_DIR, "socket.json")
        if os.path.exists(cache_path):
            sock_dict = {}
            with open(cache_path, "r") as f:
                sock_dict = json.load(f)
            if sock_dict:
                return sock_dict
        
        return 
        
    def _store_socket(self):
        """ store capture socket and cache socket in file """
        cache_path = os.path.join(self.DB_DIR, "socket.json")
        
        content = {
            "capture": self.REDIS_CAPTURE_DB_SOCKET,
            "cache": self.REDIS_CACHE_DB_SOCKET
        }
        
        if not os.path.exists(cache_path):
            with open(cache_path, "w") as f:
                json.dump(content, f, indent=4)
    
    
if __name__ == "__main__":
    context = Context()
    pass